﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KeshaParser
{
    class Scanner
    {
        private int currCharIndex;
        private char currChar;
        private string text;

        private delegate bool ReadStringCondition();

        public void SetText(string text)
        {
            Reset();
            this.text = text;
        }

        public Token GetNextToken()
        {
            if (currCharIndex >= text.Length)
            {
                return Token.CreateEofToken();
            }

            UpdateCurrChar();
            Token t = Scan();
            ++currCharIndex;

            return t;
        }
        
        public string GetCurrentPositionAsString()
        {
            return TextUtils.CharPosToText(text, currCharIndex);
        }

        private void Reset()
        {
            currCharIndex = 0;
        }

        private Token Scan()
        {
            SkipWhiteSpaces();
            return ReadToken();
        }

        private void SkipWhiteSpaces()
        {
            while (char.IsWhiteSpace(currChar))
            {
                PeekNext();

                if (currCharIndex == text.Length)
                {
                    return;
                }
            }
        }

        private Token ReadToken()
        {
            return DefaultSwitchBranch();
        }

        private Token DefaultSwitchBranch()
        {
            if (currChar.InAlphabet())
            {
                string str = ReadInditifier();                
                return GetKeywordToken(str);
            }

            if (currCharIndex == text.Length)
            {
                return Token.CreateEofToken();
            }

            return GetErrorToken();
        }

        private Token GetErrorToken()
        {
            string errMsg = "at " + GetCurrentPositionAsString() + ": Unknown lexeme";
            return new Token(TokenType.ERROR, errMsg);
        }

        private string ReadInditifier()
        {
            return ReadStringWhileCond(() => currChar.InAlphabet());
        }
        
        private string ReadStringWhileCond(ReadStringCondition cond)
        {
            string str = "" + currChar;
            PeekNext();

            while (cond() && currCharIndex < text.Length)
            {
                str += currChar;
                PeekNext();
            }
            PeekPrev();

            return str;
        }

        private Token GetKeywordToken(string str)
        {
            return KeywordTokenCreator.CreateByString(str);
        }

        private char GetNextChar()
        {
            return (currCharIndex + 1 < text.Length) ? text[currCharIndex + 1] : ' ';
        }

        private void PeekNext()
        {
            ++currCharIndex;
            UpdateCurrChar();
        }

        private void PeekPrev()
        {
            --currCharIndex;
            UpdateCurrChar();
        }

        private void UpdateCurrChar()
        {
            if (currCharIndex < text.Length)
            {
                currChar = text[currCharIndex];
            }
        }
    }
}
